﻿using System;
using System.Reflection;

namespace PGMapper
{
    /// <summary>
    /// Declares that a field of an enum represents a particular string value in the database.
    /// </summary>
    [AttributeUsage(AttributeTargets.Enum | AttributeTargets.Field, AllowMultiple=false)]
    public class EnumValueAttribute : Attribute
    {
        /// <summary>
        /// The strings in the database that represent this enum value.
        /// </summary>
        public string[] Values { get; private set; }

        /// <param name="value">The strings in the database that represent this enum value.</param>
        public EnumValueAttribute(params string[] values)
        {
            this.Values = values;
        }

        private static EnumValueAttribute GetOn(FieldInfo field)
        {
            object[] atts = field.GetCustomAttributes(typeof(EnumValueAttribute), false);
            return atts.Length > 0 ? (EnumValueAttribute)atts[0] : null;
        }

        /// <summary>
        /// Converts the given string value into an instance of the given Enum type, only if
        /// the Enum type has a member with an [EnumValue] attribute matching the given string.
        /// Otherwise returns the value unchanged.
        /// </summary>
        public static object ConvertToEnumIfApplied(Type type, string value)
        {
            foreach (FieldInfo field in type.GetFields(BindingFlags.Public | BindingFlags.Static))
            {
                EnumValueAttribute att = GetOn(field);
                if (att == null) continue;
                
                foreach (var v in att.Values)
                    if (v == value) 
                        return field.GetValue(null);
            }
            return value;
        }

        /// <summary>
        /// Converts the given enum value into a string, only if the enum field has an [EnumValue]
        /// attribute applied to it.
        /// </summary>
        public static object ConvertToStringIfApplied(Type type, object value)
        {
            FieldInfo field = type.GetField(value.ToString(), BindingFlags.Public | BindingFlags.Static);
            EnumValueAttribute att = GetOn(field);

            if (att != null) return att.Values[0];
            return value;
        }

        /// <summary>
        /// Gets the string representation (as defined by the [EnumValue] attribute) of the given
        /// enum value.
        /// </summary>
        public static string StringValue(Enum e)
        {
            return e != null ? (string)ConvertToStringIfApplied(e.GetType(), e) : null;
        }
    }
}
